Conversation
722618c to
ac6ca05
Compare
|
CI failure will be taken care of by #1544 |
They still get paid at every kernel startup after this PR. The debugger information is needed at the latest when generating The import time of A benchmark showing the benefits would help here. Otherwise, there is a number of regressions here:
|
krassowski
left a comment
There was a problem hiding this comment.
As per comment above. Thank you for working on it!
|
Good point for subclasses, I'll see what I can do. It's true that I tested only with #1542; that is minimal up to startup point and shut off immediately; and for import only for kernelapp.
Fair; I did not think about this – but I would not consider kernel_info_reply to be part of startup; I need to check but at least the startup before the first kernel_info_request is faster, I'm assuming the first kernel_info_request is sent only once the kernel is started (I'll verify), so I still think it's a (partial) win, and this should make the time to ready. But I least that's fixable separately and only affect the I'm also profiling locally with modified jupyter_client, traitlets and IPython (to defer more stuff from there as well); and yes some thing like psutils are otherwise still in startup path via other packages; but I can try to publish profiles even with modified versions to give an idea of what can be achieved. |
|
I suppose another big things, is are we ok dropping debugger_class traitlet and deprecating it to at least have some way of making debugger import lazy ? |
|
See #1545 for how I think we can handle making kerne_info_reply faster by providing a config option. |
|
Here are the result of With #1542 merged in; to stop the kernel immediately and make it measurable. Also requires IPython'm main; traitlet and jupyter_client on latest releases
|
|
Also technically you should test with |
|
for Though it becomes brittle as there is no more guard in |
ac6ca05 to
c03f515
Compare
Constructing the debugger in `IPythonKernel.__init__` imported debugpy on every kernel startup, roughly 50ms, even though most sessions never debug. Create it lazily on first use instead. `debugger_class` is what made that awkward: traitlets resolves a `Type` trait's string default -- and therefore imports it -- as soon as the owning HasTraits object is created. But it does so in exactly one place, `instance_init`; `validate`, `info` and `default_value_repr` are all on demand. A small `LazyType` subclass that skips `instance_init` and resolves on first read or write is therefore lazy while remaining a perfectly ordinary trait, so subclass overrides, `IPythonKernel(debugger_class=X)`, assignment and config all keep working unchanged. Setting `debugger_class` explicitly, by kwarg or config, builds the debugger eagerly: the class is already imported at that point, so there is nothing left to defer, and a bad class fails at construction rather than at the first debug request. Two things the lazy path has to be careful about, since the debugger is no longer guaranteed to exist by the time `start()` runs: * `poll_stopped_queue` used to be scheduled unconditionally in `start()`. It now goes through an idempotent `_ensure_stopped_queue_poll()` called from lazy creation, from the `debugger` setter and from `start()`, since any of the three can complete the set of things the poll needs. Without this, assigning `kernel.debugger` -- which subclasses do to substitute their own debugger -- would silently stop reporting breakpoints. * The flag recording that initialisation was attempted is set only once the debugger has been constructed, and a failing constructor is logged before being re-raised. Otherwise the first debug request would get the exception and every later one would quietly get a None reply instead.
Setting IPYKERNEL_BENCHMARK_STARTUP_SHUTDOWN stops the event loop right after it starts, so total process wall-clock time measures kernel startup without needing an actual client to send a shutdown request. This can be used in conjunction with -X importtime and profile viewer like tuna to work on speeding up importing and starting a kernel
c03f515 to
7aac75f
Compare
ad8e22c to
eeb87d1
Compare
|
this now uses a custom local |


Both imports were paid for on every kernel startup even though most sessions never debug or ask for usage information.
IPythonKernel.debuggeris now a lazily-created property; the debugger (and the debugpy import) is only built on the first debug request.poll_stopped_queueis scheduled at that point rather than instart().debugger_classwas aTypetrait, which traitlets resolves — and therefore imports — as soon as the kernel is instantiated. It is replaced by a custom LazyType traitOn a local test (where I have optimisation in IPython and traitlets as well), this brings the startup time from 220ms to 170ms